
	/************************************************************************************************

		@API
		Class: jsl.File

		Constructor:
			file	File					(path*)

		Base:
			file	create				(overwrite*)
			file	remove				()
			file	rename				(name, overwrite*)
			data	read					(n*)
			file	write					(data, n*)
			file	append				(data, n*)
			b			compare				(path)
			file	truncate			(size)
			file	copy					(path, overwrite*)
			file	move					(path, overwrite*)
			file 	add						(path)

		Info:
			n			size					()
			b			exists				()
			b			empty					()

			b			isFile				()
			b			isDir					()

			name	name					()
			path	path					()
			path	nativePath		()
			path	parent				()

			s			toString			()

		Status:
			file	rseek					(n)
			file	seek					(ix*)
			ix		index					()

		Note:
			no open, no close (more abstraction)

		Legend:
			data		blob | array | string | number
			name		string
			path		relative/absolute path | File instance | Dir instance
			file		File instance

	************************************************************************************************/

	jsl.Class("jsl.File", {

		//Data
		//---------------------------------------------------------------------------
		$__ix: 	0,
		$__tid:	0,

		//---------------------------------------------------------------------------
		__ix:  		0,
		__f: 			null,
		__nf:			null,

		//---------------------------------------------------------------------------
		initializer: function() {
			__ni_fle_init && __ni_fle_init(this)

			//---------------------------------------------------------------------------
			this.name = function(path) {//!
				if(arguments.length !== 1)	throw jsl.ParamError()//!
				return this.__nme(path)
			}
		},

		//Constructor
		//---------------------------------------------------------------------------
		File: function(path) {
			var tmp//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(arguments.length)
				this.__f = jsl.File.__path(path)
			else {

				tmp = jsl.Dir.tmpDir()//!
				if(!tmp.exists())	throw jsl.ParamError("dir.TmpNotFound", [ tmp ])//!

				this.__f = tmp + "/f" + jsl.File.__tid++
			}
		},
		//---------------------------------------------------------------------------
		$create: function(path, overwrite) {
			var file//!
			if(arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length) 	file = new this(this.__path(path)) //<-stacktrace clean
			else 									file = new this

			this.__create(file + "", arguments.length === 2 ? overwrite : false)
			return file
		},
		//---------------------------------------------------------------------------
		$remove: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			this.__remove(path)
			return new this(path)
		},
		//---------------------------------------------------------------------------
		$rename: function(path, name, overwrite) {//!
			if(arguments.length < 2 || arguments.length > 3)	throw jsl.ParamError()//!
			return new this(this.__rename.apply(this, arguments))
		},
		//---------------------------------------------------------------------------
		$read: function(path, n) {//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1)	return this.__read(path, 0)
			else												return this.__read(path, 0, n)
		},
		//---------------------------------------------------------------------------
		$write: function(path, data, n) {//!
			if(arguments.length < 2 || arguments.length > 3)	throw jsl.ParamError()//!

			if(arguments.length === 2)	this.__write(path, data, 0)
			else												this.__write(path, data, 0, n)
			return new this(path)
		},
		//---------------------------------------------------------------------------
		$append: function(path, data, n) {//!
			if(arguments.length < 2 || arguments.length > 3)	throw jsl.ParamError()//!
			this.__append.apply(this, arguments)
			return new this(path)
		},
		//---------------------------------------------------------------------------
		$compare: function(path, path2) {//!
			if(arguments.length !== 2)	throw jsl.ParamError()//!
			return this.__compare(path, this.__path(path2, "path2"))
		},
		//---------------------------------------------------------------------------
		$truncate: function(path, size) {//!
			if(arguments.length !== 2)	throw jsl.ParamError()//!
			this.__truncate(path, size)
			return new this(path)
		},
		//---------------------------------------------------------------------------
		$copy: function(path, path2, overwrite) {//!
			if(arguments.length < 2 || arguments.length > 3)	throw jsl.ParamError()//!

			if(arguments.length === 2)	this.__copy(path, this.__path(path2, "path2"))
			else												this.__copy(path, this.__path(path2, "path2"), overwrite)
			return new this(path2)
		},
		//---------------------------------------------------------------------------
		$move: function(path, path2, overwrite) {//!
			if(arguments.length < 2 || arguments.length > 3)	throw jsl.ParamError()//!

			if(arguments.length === 2)	this.__move(path, this.__path(path2, "path2"))
			else												this.__move(path, this.__path(path2, "path2"), overwrite)
			return new this(path2)
		},
		//---------------------------------------------------------------------------
		$add: function(path, path2) {//!
			if(arguments.length !== 2)	throw jsl.ParamError()//!
			this.__add(path, this.__path(path2, "path2"))
			return new this(path)
		},
		//---------------------------------------------------------------------------
		$size: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__size(path)
		},
		//---------------------------------------------------------------------------
		$exists: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return !!this.__exists(path)
		},
		//---------------------------------------------------------------------------
		$isDir: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__exists(path) === 2
		},
		//---------------------------------------------------------------------------
		$isFile: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__exists(path) === 1
		},
		//---------------------------------------------------------------------------
		$empty: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return !this.__size(path)
		},
		//---------------------------------------------------------------------------
		$path: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__path(path)
		},
		//---------------------------------------------------------------------------
		$nativePath: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__nativePath(path)
		},
		//---------------------------------------------------------------------------
		$parent: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return this.__parent(path)
		},

		//Regular:
		//---------------------------------------------------------------------------
		create: function(overwrite) {//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(arguments.length)	jsl.File.__create(this.__f, overwrite)
			else									jsl.File.__create(this.__f)

			this.__ix = 0
			return this
		},
		//---------------------------------------------------------------------------
		remove: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			jsl.File.__remove(this.__f)
			return this
		},
		//---------------------------------------------------------------------------
		rename: function(name, overwrite) {//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1) 	return new jsl.File(jsl.File.__rename(this.__f, name))
			else												return new jsl.File(jsl.File.__rename(this.__f, name, overwrite))
		},
		//---------------------------------------------------------------------------
		read: function(n) {
			var data//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(!arguments.length)	data = jsl.File.__read(this.__f, this.__ix)
			else									data = jsl.File.__read(this.__f, this.__ix, n)

			if(arguments.length)	this.__ix += n
			else if(data)					this.__ix += data.size()
			return data
		},
		//---------------------------------------------------------------------------
		write: function(data, n) {
			var nb//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1)	jsl.File.__write(this.__f, data, this.__ix)
			else												jsl.File.__write(this.__f, data, this.__ix, n)

			if(jsl.isNumber(data))									nb = 4
			else if(data instanceof jsl.Data.Blob)	nb = data.size()
			else																		nb = data.length

			if(arguments.length === 2 && n < nb) nb = n
			this.__ix += nb
			return this
		},
		//---------------------------------------------------------------------------
		append: function(data, n) {//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1)	jsl.File.__append(this.__f, data)
			else												jsl.File.__append(this.__f, data, n)

			return this
		},
		//---------------------------------------------------------------------------
		compare: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			return jsl.File.__compare(this.__f, path)
		},
		//---------------------------------------------------------------------------
		truncate: function(size) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			jsl.File.__truncate(this.__f, size)
			return this
		},
		//---------------------------------------------------------------------------
		copy: function(path, overwrite) {//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1)	jsl.File.__copy(this.__f, path)
			else												jsl.File.__copy(this.__f, path, overwrite)

			return new jsl.File(path)
		},
		//---------------------------------------------------------------------------
		move: function(path, overwrite) {//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()//!

			if(arguments.length === 1)	return jsl.File.__move(this.__f, path)
			else												return jsl.File.__move(this.__f, path, overwrite)

			return new jsl.File(path)
		},
		//---------------------------------------------------------------------------
		add: function(path) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()//!
			jsl.File.__add(this.__f, path)
			return this
		},
		//---------------------------------------------------------------------------
		size: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return jsl.File.__size(this.__f)
		},
		//---------------------------------------------------------------------------
		exists: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return !!jsl.File.__exists(this.__f)
		},
		//---------------------------------------------------------------------------
		isDir: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return jsl.File.__exists(this.__f) === 2
		},
		//---------------------------------------------------------------------------
		isFile: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return jsl.File.__exists(this.__f) === 1
		},
		//---------------------------------------------------------------------------
		empty: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return !jsl.File.__size(this.__f)
		},
		//---------------------------------------------------------------------------
		name: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return jsl.File.__nme(this.__f)
		},
		//---------------------------------------------------------------------------
		path: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return jsl.File.__path(this.__f)
		},
		//---------------------------------------------------------------------------
		nativePath: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return jsl.File.__nativePath(this.__f)
		},
		//---------------------------------------------------------------------------
		parent: function() {//!
			if(arguments.length) throw jsl.ParamError()//!
			return jsl.File.__parent(this.__f)
		},
		//---------------------------------------------------------------------------
		rseek: function(n) {//!
			if(arguments.length !== 1) throw jsl.ParamError()
			jsl.validate(n, "i", "n")
			if(jsl.File.__exists(this.__f) !== 1)	throw jsl.ParamError("file.NotFound")
			if(this.__ix + n < 0)									throw jsl.ParamError("file.BadSeek")//!
			this.__ix += n
			return this
		},
		//---------------------------------------------------------------------------
		seek: function(ix) {
			var i//!
			if(arguments.length > 1) 							throw jsl.ParamError()
			if(arguments.length)									jsl.validate(ix, "i", "ix")
			if(jsl.File.__exists(this.__f) !== 1)	throw jsl.ParamError("file.NotFound")//!

			i = ix >= 0 ? ix : jsl.File.__size(this.__f) + (ix || 0)//!
			if(i < 0) throw jsl.ParamError("file.BadSeek")//!

			this.__ix = i
			return this
		},
		//---------------------------------------------------------------------------
		index: function() {//!
			if(arguments.length) throw jsl.ParamError()
			if(jsl.File.__exists(this.__f) !== 1)	throw jsl.ParamError("file.NotFound")//!
			return this.__ix
		},
		//---------------------------------------------------------------------------
		toString: function() {
			//no arguments.length check
			return this.__f
		}
	})

	//hidden: no stacktrace
	//---------------------------------------------------------------------------
	jsl.File.__create = function(path, overwrite) {
		var f, i, p

		f	= this.__path(path)
		i	= this.__exists(f)

		//!
		if(i === 2)						throw jsl.ParamError("file.OpError"				, [ f ])
		if(i && !+overwrite) 	throw jsl.ParamError("file.CreationError"	, [ f ])

		p = this.__parent(f)
		if(this.__exists(p) !== 2)	throw jsl.ParamError("dir.PathNotFound", [ p ])
		if(arguments.length === 2)	jsl.validate(overwrite, "b", "overwrite")//!

		if(i) this.__remove(f)
		this.__ni_create(this.__nativePath(f))
	}
	//---------------------------------------------------------------------------
	jsl.File.__remove = function(path) {
		var f, i

		f	= this.__path(path)
		i	= this.__exists(f)

		//!
		if(i === 2)	throw jsl.ParamError("file.OpError", [ f ])//!
		if(i) this.__ni_remove(this.__nativePath(f))
	}
	//---------------------------------------------------------------------------
	jsl.File.__rename = function(path, name, overwrite) {
		var f, f2, i, nm

		f	= this.__path(path)//!
		i	= this.__exists(f)

		if(!i)				throw jsl.ParamError("file.NotFound", [ f ])
		if(i === 2)		throw jsl.ParamError("file.OpError"	, [ f ])
		jsl.validate(name, "sr/^[^\\\\\\/:*?\"<>|%]+$", "name")//!

		nm = name.replace(/^\s+|\s+$/g, "")//!
		if(nm.match(/^\.{1,2}$/))		throw jsl.ParamError("BadValue:name")
		if(arguments.length === 3)	jsl.validate(overwrite, "b", "overwrite")//!

		f2 = this.__parent(f) + "/" + nm

		if(this.__nme(f) !== nm) {

			i	= this.__exists(f2)

			//!
			if(i === 1 && !+overwrite)	throw jsl.ParamError("file.RenameError"	, [ f ])
			if(i === 2)									throw jsl.ParamError("file.RenameError2", [ f ])//!

			if(i === 1) this.__remove(f2)
			this.__ni_rename(this.__nativePath(f), this.__nativePath(f2))
		}

		return f2
	}
	//---------------------------------------------------------------------------
	jsl.File.__read = function(path, ix, n) {
		var f, i, sz, nb, data

		f	= this.__path(path)//!
		i	= this.__exists(f)

		if(!i)											throw jsl.ParamError("file.NotFound", [ f ])
		if(i === 2)									throw jsl.ParamError("file.OpError"	, [ f ])
		if(arguments.length === 3)	jsl.validate(n, "i+", "n")//!

		jsl.Data || jsl.require("data")

		if(n != 0) {

			sz = this.__size(f)
			nb = sz - ix

			if(nb > 0) {
				if(n && n < nb)	nb = n
				data = new jsl.Data.Blob
				data.__bs = this.__ni_read(this.__nativePath(f), ix, nb)
			}
		}

		return data || null
	}
	//---------------------------------------------------------------------------
	jsl.File.__write = function(path, data, ix, n) {
		var f, i, bs

		f	= this.__path(path)//!
		i	= this.__exists(f)

		if(!i)			throw jsl.ParamError("file.NotFound", [ f ])
		if(i === 2)	throw jsl.ParamError("file.OpError"	, [ f ])

		jsl.validate(data,"t[i,s,al,Blob]", "data")
		if(arguments.length === 4)			jsl.validate(n, "i+", "n")
		if(jsl.isNumber(data) && n > 4)	throw jsl.ParamError("BadValue:n")//!

		jsl.Data || jsl.require("data")

		if(n != 0) {

			try {
				if(jsl.isNumber(data))	bs = [ (data & 0xff), (data & 0xff00) >>> 8, (data & 0xff0000) >>> 16, data >>> 24 ]
				else										bs = jsl.Data.Blob.toArray(data)
			}
			catch(e) {
				throw e instanceof jsl.ParamError ? jsl.ParamError("BadValue:data") : e
			}

			if(bs.length) {
				if(n) bs = bs.slice(0, n)
				this.__ni_write(this.__nativePath(f), ix, bs)
			}
		}
	}
	//---------------------------------------------------------------------------
	jsl.File.__append = function(path, data, n) {
		var args

		if(arguments.length === 2)	args = [ path, data, this.__size(path) ]
		else												args = [ path, data, this.__size(path), n ]

		try 			{ this.__write.apply(this, args) 															}
		catch(e)	{ throw e instanceof jsl.ParamError ? e.clearStackTrace() : e }
	}
	//---------------------------------------------------------------------------
	jsl.File.__compare = function(path, path2) {
		var f1, f2, i1, i2, b, np1, np2

		f1 = this.__path(path)
		f2 = this.__path(path2)
		i1 = this.__exists(f1)
		i2 = this.__exists(f2)//!
		if(i1 === 2)	throw jsl.ParamError("file.OpError", [ f1 ])
		if(i2 === 2)	throw jsl.ParamError("file.OpError", [ f2 ])//!

		np1 = this.nativePath(f1)
		np2 = this.nativePath(f2)

		if(!i1 || !i2)				b = false
		else if(np1 === np2)	b = true
		else									b = this.__read(f1, 0) + "" === this.__read(f2, 0) + ""

		return b
	}
	//---------------------------------------------------------------------------
	jsl.File.__truncate = function(path, size) {
		var f, i, sz

		f 	= this.__path(path)
		sz 	= this.__size(f)//!
		i 	= this.__exists(f)
		if(!i)			throw jsl.ParamError("file.NotFound", [ f ])
		if(i === 2)	throw jsl.ParamError("file.OpError"	, [ f ])
		jsl.validate(size, "i+", "size")
		if(size > sz)	throw jsl.ParamError("BadValue:size")//!

		if(size < sz)
			this.__ni_truncate(this.__nativePath(f), size)
	}
	//---------------------------------------------------------------------------
	jsl.File.__copy = function(path, path2, overwrite) {
		var f1, f2, i1, i2

		f1 = this.__path(path)
		f2 = this.__path(path2)
		i1 = this.__exists(f1)
		i2 = this.__exists(f2)//!
		if(!i1)											throw jsl.ParamError("file.NotFound"		, [ f1 ])
		if(i1 === 2)								throw jsl.ParamError("file.OpError"			, [ f1 ])
		if(i2 === 1 && !+overwrite)	throw jsl.ParamError("file.CopyError"		, [ f1 ])
		if(i2 === 2)								throw jsl.ParamError("file.CopyError2"	, [ f1 ])
		if(arguments.length === 3)	jsl.validate(overwrite, "b", "overwrite")//!

		if(i2)	this.__remove(f2)
		this.__ni_copy(this.__nativePath(f1), this.__nativePath(f2))
	}
	//---------------------------------------------------------------------------
	jsl.File.__move = function(path, path2, overwrite) {
		var f1, f2, i1, i2

		f1 = this.__path(path)
		f2 = this.__path(path2)
		i1 = this.__exists(f1)
		i2 = this.__exists(f2)//!
		if(!i1)											throw jsl.ParamError("file.NotFound"		, [ f1 ])
		if(i1 === 2)								throw jsl.ParamError("file.OpError"			, [ f1 ])
		if(i2 === 1 && !+overwrite)	throw jsl.ParamError("file.MoveError"		, [ f2 ])
		if(i2 === 2)								throw jsl.ParamError("file.MoveError2"	, [ f2 ])
		if(arguments.length === 3)	jsl.validate(overwrite, "b", "overwrite")//!

		if(i2)	this.__remove(f2)
		this.__ni_move(this.__nativePath(f1), this.__nativePath(f2))
	}
	//---------------------------------------------------------------------------
	jsl.File.__add = function(path, path2) {
		var f1, f2, i1, i2

		f1 = this.__path(path)
		f2 = this.__path(path2)
		i1 = this.__exists(f1)
		i2 = this.__exists(f2)

		//!
		if(!i1)				throw jsl.ParamError("file.NotFound"	, [ f1 ])
		if(i1 === 2)	throw jsl.ParamError("file.OpError"		, [ f1 ])
		if(i2 === 2)	throw jsl.ParamError("file.AddError"	, [ f2 ])//!

		if(i2) this.__ni_add(this.__nativePath(f1), this.__nativePath(f2))
	}
	//---------------------------------------------------------------------------
	jsl.File.__size = function(path) {
		var file, i

		file 	= this.__path(path)//!
		i			= this.__exists(file)

		if(!i)			throw jsl.ParamError("file.NotFound", [ file ])
		if(i === 2)	throw jsl.ParamError("file.OpError"	, [ file ])//!

		return this.__ni_size(this.__nativePath(file))
	}
	//---------------------------------------------------------------------------
	jsl.File.__exists = function(path) {
		return this.__ni_exists(this.__nativePath(path))
	}
	//---------------------------------------------------------------------------
	jsl.File.__empty = function(path) {
		return !this.__size(path)
	}
	//---------------------------------------------------------------------------
	jsl.File.__nme = function(path) {
		var f, ts

		f		= this.__path(path)
		ts 	= f.split("/")

		return (ts[ts.length - 1] || ts[ts.length - 2] || "/").replace(/:+$/, "")
	}
	//---------------------------------------------------------------------------
	jsl.File.__path = function(path) {
		return this.__path(path)
	}
	//---------------------------------------------------------------------------
	jsl.File.__nativePath = function(path) {
		var p, np

		p = this.__path(path)

		if(p.indexOf(":") !== -1)	{
			np = p.split(":")
			np = jsl.Dir.root(np[0]) + (np[1] || "")
		}
		else
			return np = p

		return np
	}
	//---------------------------------------------------------------------------
	jsl.File.__parent = function(path) {
		var p

		p	= this.__path(path)
		p = p.split("/").slice(0, -1).join("/")
		return p || ""
	}
